/*
 * Copyright 2008 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.google.android.apps.mytracks.endtoendtest;

import com.google.android.apps.mytracks.TrackListActivity;
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
import com.google.android.apps.mytracks.stats.TripStatistics;

import android.annotation.TargetApi;
import android.app.Instrumentation;
import android.location.Location;
import android.test.ActivityInstrumentationTestCase2;
import android.util.Log;

/**
 * Tests the statstics of track.
 * 
 * @author Youtao Liu
 */
public class StatsticsTest extends ActivityInstrumentationTestCase2<TrackListActivity> {

  private Instrumentation instrumentation;
  private TrackListActivity activityMyTracks;

  private double MIN_DIFFERENCE = 0.3;

  @TargetApi(15)
  public StatsticsTest() {
    super(TrackListActivity.class);
  }

  @Override
  protected void setUp() throws Exception {
    super.setUp();
    instrumentation = getInstrumentation();
    activityMyTracks = getActivity();
    EndToEndTestUtils.setupForAllTest(instrumentation, activityMyTracks);
  }

  /**
   * Tests the computation of track statistics.
   */
  public void testSimpleTrack() {
    int gpsSignalNumber = 200;

    // Start recording
    long startTime = System.currentTimeMillis();
    EndToEndTestUtils.startRecording();
    EndToEndTestUtils.sendGps(gpsSignalNumber, 0, 100);

    // Stop recording
    long stopTime = System.currentTimeMillis();
    EndToEndTestUtils.stopRecording(true);

    long expectTotalTime = stopTime - startTime;
    TripStatistics tripStatistics = getLastTrackStatistics();
    // Measure it from Google Maps from -1.3,51 to -1.2005,51.0995 (meters).
    double expectDistance = distanceBetweeen(EndToEndTestUtils.START_LATITUDE,
        EndToEndTestUtils.START_LONGITUDE, EndToEndTestUtils.START_LATITUDE + (gpsSignalNumber - 1)
            * EndToEndTestUtils.DELTA_LADITUDE, EndToEndTestUtils.START_LONGITUDE
            + (gpsSignalNumber - 1) * EndToEndTestUtils.DELTA_LONGITUDE);
    checkStatistics(expectTotalTime, expectTotalTime, expectDistance, tripStatistics);
  }

  /**
   * Tests the computation of track statistics when there is some noise
   * locations.
   */
  public void testSimpleTrack_withNoiseSignals() {
    int gpsSignalNumber = 200;

    // Start recording
    long startTime = System.currentTimeMillis();
    EndToEndTestUtils.startRecording();
    sendGpsWithNoise(gpsSignalNumber, 0, 100);

    // Stop recording
    long stopTime = System.currentTimeMillis();
    EndToEndTestUtils.stopRecording(true);

    long expectTotalTime = stopTime - startTime;
    TripStatistics tripStatistics = getLastTrackStatistics();
    // Measure it from Google Maps from -1.3,51 to -1.2005,51.0995 (meters).
    double expectDistance = distanceBetweeen(EndToEndTestUtils.START_LATITUDE,
        EndToEndTestUtils.START_LONGITUDE, EndToEndTestUtils.START_LATITUDE + (gpsSignalNumber - 1)
            * EndToEndTestUtils.DELTA_LADITUDE, EndToEndTestUtils.START_LONGITUDE
            + (gpsSignalNumber - 1) * EndToEndTestUtils.DELTA_LONGITUDE);
    checkStatistics(expectTotalTime, expectTotalTime, expectDistance, tripStatistics);
  }

  /**
   * Tests the computation of track statistics, and the track is paused and
   * resumed during recording. And waitig some seconds during recording.
   */
  public void testPausedTrack() {
    int gpsSignalNumber = 100;

    // Start recording
    long startTime = System.currentTimeMillis();
    EndToEndTestUtils.startRecording();
    EndToEndTestUtils.sendGps(gpsSignalNumber / 2, 0, 100);

    // Wait some seconds in the same location
    long waitStartTime = System.currentTimeMillis();
    for (int i = 0; i < 5; i++) {
      EndToEndTestUtils.sleep(1000);
      EndToEndTestUtils.sendGps(1, gpsSignalNumber / 2, 10);
    }
    long waitStopTime = System.currentTimeMillis();

    EndToEndTestUtils.sendGps(gpsSignalNumber / 2, gpsSignalNumber / 2, 100);
    // Pause
    long pauseTime = System.currentTimeMillis();
    EndToEndTestUtils.pauseRecording();
    // Send Gps signal after pause.
    EndToEndTestUtils.sendGps(gpsSignalNumber / 2, gpsSignalNumber, 100);

    // Resume
    long resumeTime = System.currentTimeMillis();
    EndToEndTestUtils.resumeRecording();
    EndToEndTestUtils.sendGps(gpsSignalNumber, gpsSignalNumber, 100);

    // Stop recording
    long stopTime = System.currentTimeMillis();
    EndToEndTestUtils.stopRecording(true);

    long expectTotalTime = stopTime - startTime - (resumeTime - pauseTime);
    long expectMovingTime = expectTotalTime - (waitStopTime - waitStartTime);

    TripStatistics tripStatistics = getLastTrackStatistics();
    double expectDistance = distanceBetweeen(EndToEndTestUtils.START_LATITUDE,
        EndToEndTestUtils.START_LONGITUDE, EndToEndTestUtils.START_LATITUDE
            + (gpsSignalNumber * 2 - 1) * EndToEndTestUtils.DELTA_LADITUDE,
        EndToEndTestUtils.START_LONGITUDE + (gpsSignalNumber * 2 - 1)
            * EndToEndTestUtils.DELTA_LONGITUDE);
    checkStatistics(expectTotalTime, expectMovingTime, expectDistance, tripStatistics);
  }

  /**
   * Tests the computation of track statistics, and the track is around a square
   * area.
   */
  public void testTrack_squareArea() {
    int gpsSignalNumber = 10;

    // Start recording
    long startTime = System.currentTimeMillis();
    EndToEndTestUtils.startRecording();
    EndToEndTestUtils.sendGps(gpsSignalNumber, 0, 0, 1000, 0, EndToEndTestUtils.DELTA_LADITUDE);
    EndToEndTestUtils.sendGps(gpsSignalNumber, 0, gpsSignalNumber, 1000,
        EndToEndTestUtils.DELTA_LONGITUDE, 0);
    EndToEndTestUtils.sendGps(gpsSignalNumber, gpsSignalNumber, gpsSignalNumber, 1000, 0,
        -EndToEndTestUtils.DELTA_LADITUDE);
    EndToEndTestUtils.sendGps(gpsSignalNumber + 1, gpsSignalNumber, 0, 1000,
        -EndToEndTestUtils.DELTA_LONGITUDE, 0);
    // Stop recording
    long stopTime = System.currentTimeMillis();
    EndToEndTestUtils.stopRecording(true);
    long expectTotalTime = stopTime - startTime;

    TripStatistics tripStatistics = getLastTrackStatistics();
    // (width + height) * 2
    double expectDistance = (distanceBetweeen(EndToEndTestUtils.START_LATITUDE,
        EndToEndTestUtils.START_LONGITUDE, EndToEndTestUtils.START_LATITUDE,
        EndToEndTestUtils.START_LONGITUDE + (gpsSignalNumber - 1)
            * EndToEndTestUtils.DELTA_LONGITUDE) + distanceBetweeen(
        EndToEndTestUtils.START_LATITUDE,
        EndToEndTestUtils.START_LONGITUDE,
        EndToEndTestUtils.START_LATITUDE + (gpsSignalNumber - 1) * EndToEndTestUtils.DELTA_LADITUDE,
        EndToEndTestUtils.START_LONGITUDE)) * 2;
    checkStatistics(expectTotalTime, expectTotalTime, expectDistance, tripStatistics);
  }

  /**
   * Checks the expect value with real track statistics.
   * 
   * @param expectTotalTime expected total time
   * @param expectMovingTime expected moving time
   * @param expectDistance expected total distance
   * @param tripStatistics the real statistics
   */
  private void checkStatistics(long expectTotalTime, long expectMovingTime, double expectDistance,
      TripStatistics tripStatistics) {
    Log.i(EndToEndTestUtils.LOG_TAG, "Expect total time:" + expectTotalTime
        + "; Expect moving time:" + expectMovingTime + "; Expect total distance:" + expectDistance);
    Log.i(
        EndToEndTestUtils.LOG_TAG,
        "Total time:" + tripStatistics.getTotalTime() + "; Moving time:"
            + tripStatistics.getMovingTime() + "; Total distance:"
            + tripStatistics.getTotalDistance());
    assertTrue(Math.abs((expectTotalTime - tripStatistics.getTotalTime()) / expectTotalTime) < MIN_DIFFERENCE);
    assertTrue(Math.abs((expectDistance - tripStatistics.getTotalDistance()) / expectDistance) < MIN_DIFFERENCE);
    assertTrue(Math.abs((expectMovingTime - tripStatistics.getMovingTime()) / expectMovingTime) < MIN_DIFFERENCE);
  }

  /**
   * Gets the TripStatistics of last track.
   * 
   * @return the TripStatistics of last track
   */
  private static TripStatistics getLastTrackStatistics() {
    MyTracksProviderUtils providerUtils = MyTracksProviderUtils.Factory
        .get(EndToEndTestUtils.activityMytracks.getApplicationContext());
    long trackId = providerUtils.getLastTrack().getId();

    return providerUtils.getTrack(trackId).getTripStatistics();
  }

  /**
   * Gets the distance between two Gps locations.
   * 
   * @param lat1 latitude of location1
   * @param lng1 longitude of location1
   * @param lat2 latitude of location2
   * @param lng2 longitude of location2
   * @return the distance
   */
  private static Double distanceBetweeen(double lat1, double lng1, double lat2, double lng2) {
    float[] results = new float[2];
    Location.distanceBetween(lat1, lng1, lat2, lng2, results);
    double distance = (double) results[0];
    return distance;
  }

  /**
   * Sends Gps signals. Sends an abnormal Gps signal in every ten signals.
   * 
   * @param totalNumber send times
   * @param offset is used to compute the start latitude and longitude
   * @param pause pause interval between each sending
   */
  private void sendGpsWithNoise(int totalNumber, int offset, int pause) {
    int numberOfEachSend = 10;
    int i = 0;
    for (; i < totalNumber / numberOfEachSend; i++) {
      int startOffset = offset + i * numberOfEachSend;
      EndToEndTestUtils.sendGps(numberOfEachSend, startOffset, pause);
      // This is a noise signal.
      EndToEndTestUtils.sendGps(1, startOffset, startOffset + numberOfEachSend * 2, pause,
          EndToEndTestUtils.DELTA_LONGITUDE, EndToEndTestUtils.DELTA_LADITUDE);
    }
    EndToEndTestUtils.sendGps(totalNumber / numberOfEachSend, offset + i * numberOfEachSend, pause);
  }

}
